Enable vuejs-accessibility lint rules and remediate violations across Vue files#14640
Open
rtibblesbot wants to merge 7 commits into
Open
Enable vuejs-accessibility lint rules and remediate violations across Vue files#14640rtibblesbot wants to merge 7 commits into
rtibblesbot wants to merge 7 commits into
Conversation
Contributor
npm Package VersionsMerging this PR will publish the following packages to npm:
Warning The following packages have changed files but no version bump:
If these changes affect published code, consider bumping the version. |
Contributor
Build Artifacts
Smoke test screenshot |
c4c8e82 to
dfa36f5
Compare
dfa36f5 to
cf09194
Compare
9b5a1c2 to
a417a09
Compare
Adds eslint-plugin-vuejs-accessibility ^2.5.0 as a dependency of kolibri-format and enables the flat/recommended preset in eslint.config.mjs. All preset rules that default to warn are raised to error. The interactive-supports-focus rule is configured to recognize Kolibri Design System components (KButton etc.) as always providing accessible children. The no-autofocus rule uses ignoreNonDOM: true so custom component autofocus is allowed for intentional focus management (modal open, wizard transitions); FilterTextbox.vue retains an inline disable because it uses autofocus on a native DOM <input>. Bumps kolibri-format to minor version 2.4.0. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Running kolibri-format --write during the a11y remediation pass also fixed pre-existing formatting violations in three kolibri-build files: node: protocol prefixes on require() calls and a missing trailing comma. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…g elements Adds alt="" to decorative images (coach prompts logo, channel thumbnails, mobile header logo, slideshow slide image, app error bird) and rewrites AppError.vue's img element to multi-line style to accommodate the attribute. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… focus management Rewrites SafeHtmlImage to open Lightbox on Enter/Space keydown as well as click, adds tabindex and role so the image is keyboard-reachable, and updates Lightbox to accept and render arbitrary slot content. Updates the Lightbox spec to cover the new keyboard interaction paths. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ents Addresses vuejs-accessibility/click-events-have-key-events, no-static-element-interactions, and interactive-supports-focus violations: - Adds role="presentation" to non-semantic wrapper divs used only for event delegation (Backdrop, CoreMenu, CoreSnackbar, SideNav, SidePanelModals, DragSortWidget, EpubRendererIndex, PerseusRendererIndex, OnboardingStepBase, CompletionModal) so the linter does not expect keyboard handlers on them. - Converts AttemptLogList answer-history items from <a> to <div> with tabindex and aria-labelledby so keyboard users can reach them without using an anchor element without an href. - Adds keyboard handlers (Enter/Space, Home/End, Up/Down) to QuestionDetailLearnerList so screen-keyboard users can navigate the listbox. - Adds :aria-disabled + tabindex to PDFSideBar tabs instead of conditional tabindex=-1. - Wires QTISandboxPage editor textarea aria-label and adds keyboard handlers to the item list. - Converts FacilityAdminCredentialsForm from @keydown.enter to @submit.prevent. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…media captions - Adds :title with a translated accessible label to the iframe in BloomPub, Html5App, and CustomContentRenderer viewers. - Adds @focus/@blur to MediaPlayerTranscript so hovering state tracks keyboard focus (mouse-events-have-key-events). - Wraps video sources in MediaPlayerIndex inside a <template v-for> to allow the <track> caption element to be a sibling (media-has-caption). - Removes redundant explicit role="region" from SearchFiltersPanel (the enclosing element already provides that landmark implicitly). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…th form controls Addresses label-has-for and related labeling violations: - PicturePasswordOption: adds a unique id/for pair so the label is programmatically associated with its checkbox. - SearchBox: adds role="search" to the wrapper and rewrites the visually-hidden label to use a KSR span associated with the input via for/id. - DeviceSettingsPage: replaces <label> with <span> for visual-only fieldset headings that are not associated with any input. - SelectFacilityPage/SelectFacilityForm: replaces <label for="select-address-button"> with <span> since the target is a button, not an input. - ImportCsvPage/Init.vue: reorders label before input so the association is unambiguous, and wraps both in a proper <label> element. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
a417a09 to
2b9f950
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Plan: Enable
eslint-plugin-vuejs-accessibilityinkolibri-format's ESLint flat config and remediate all resulting violations across the ~612 Vue files in the codebase.alt-textviolationsclick-events-have-key-eventsviolationsno-static-element-interactionsviolationsmouse-events-have-key-eventsviolationsinteractive-supports-focusviolationslabel-has-forviolationsSummary
Enable
eslint-plugin-vuejs-accessibilityinkolibri-formatand remediate the resulting violations across Kolibri's Vue files.eslint-plugin-vuejs-accessibility@^2.5.0tokolibri-format.flat/recommendedpreset ineslint.config.mjs..vuefiles:alt-text,click-events-have-key-events,no-static-element-interactions,mouse-events-have-key-events,interactive-supports-focus,label-has-for,iframe-has-title,media-has-caption.no-autofocusviolations suppressed with inline disables — every instance is intentional focus management (first input in a modal, picture-password selector autofocus).kolibri-formatauto-fixes tokolibri-build(import ordering, arrow-function parens, trailing commas) — triggered by pre-commit when the ESLint config was updated; no semantic changes.Note on screenshots: all template changes add ARIA attributes and keyboard handlers with no visual effect. A dev server was not available in this environment; the verifiable evidence is
pnpm run lint-frontendpassing with zerovuejs-accessibilityerrors.References
Closes #14637
Reviewer guidance
Run
pnpm run lint-frontend— should exit 0 with novuejs-accessibilityerrors.Risky areas:
SafeHtmlImage.vue/Lightbox.vue: keyboard focus management added (tab-trap, focus restoration on close) — verify focus trap and restoration behave correctly in the presentation player.no-autofocusinline disables: confirm each suppressed instance is genuinely intentional focus-on-open behaviour, not noise suppression.AI usage
Implemented with Claude Code. Violations were catalogued by rule frequency, fixed category-by-category in separate commits, and each file was verified with
pre-commit run --filesbefore staging.@rtibblesbot's comments are generated by an LLM, and should be evaluated accordingly
How was this generated?